package com.qf.micrograss.grass.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qf.api.feign.PointFeign;
import com.qf.common.baidu.utils.BaiduTextCheckUtils;
import com.qf.common.baidu.utils.CheckResult;
import com.qf.common.core.common.PageVO;
import com.qf.common.core.common.R;
import com.qf.common.core.entity.Grass;
import com.qf.common.core.entity.GrassLike;
import com.qf.common.core.utils.UserUtils;
import com.qf.common.redis.utils.RedisUtils;
import com.qf.common.xxljob.config.XxlJobInfo;
import com.qf.common.xxljob.utils.XxlJobUtils;
import com.qf.micrograss.grass.mapper.GrassLikeMapper;
import com.qf.micrograss.grass.mapper.GrassMapper;
import com.qf.micrograss.grass.pojo.dto.GrassDTO;
import com.qf.micrograss.grass.pojo.dto.GrassSearchDTO;
import com.qf.micrograss.grass.pojo.vo.GrassVO;
import com.qf.micrograss.grass.service.IGrassService;
// import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @date 2025/11/10
 * @desc
 */
@Service
public class GrassServiceImpl implements IGrassService {

    @Autowired
    private GrassMapper grassMapper;

    @Autowired
    private PointFeign pointFeign;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private GrassLikeMapper grassLikeMapper;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private XxlJobUtils xxlJobUtils;

    @Override
    // @GlobalTransactional(rollbackFor = Exception.class )
    public void add(GrassDTO grassDTO) {

        Grass grass = new Grass();
        BeanUtils.copyProperties(grassDTO,grass);
        Integer uid = UserUtils.getUid();
        grass.setUid(uid);

        // 方案1:
        // StringBuilder imgPath1 = new StringBuilder();
        // // http://1.jpg,http://2.jpg,
        // for (int i = 0; i < grassDTO.getImgList().size(); i++) {
        //     imgPath1.append(grassDTO.getImgList().get(i));
        //     if (i !=  grassDTO.getImgList().size()-1){
        //         imgPath1.append(",");
        //     }
        // }
        // System.out.println("imgPath1 = " + imgPath1);
        // 方案2:
        // String imgPath2 = String.join(",", grassDTO.getImgList());
        // System.out.println("imgPath2 = " + imgPath2);

        // 方案3:
        String imgPath3 = grassDTO.getImgList().stream().collect(Collectors.joining(","));

        CheckResult checkResult = BaiduTextCheckUtils.textCheck(grassDTO.getTitle() + grassDTO.getContent());
        if (checkResult.getConclusionType() == 1) {
            // 审核状态(1是通过,234 不通过)
            grass.setCheckStatus(1);
            grass.setCheckMsg(checkResult.getMsg());
        } else {
            grass.setCheckStatus(checkResult.getConclusionType());//2.不合规，3.疑似，4.审核失败
            grass.setCheckMsg(checkResult.getMsg());
        }

        grass.setImgPath(imgPath3);
        if (grassDTO.getDeployTime() != null){
            grass.setStatus(0);// 未发布
        } else {
            grass.setStatus(1);
        }
        grassMapper.insert(grass);

        /*
         * 此处模拟抛出异常,方法没有正常返回,任务这个方法需要回滚
         */
        // System.out.println(1/0);

        // =================================================
        // 如果有定时发布的时间,此处就要调用xxljob的方法想xxljob-admin平台添加一个定时任务

        if (grassDTO.getDeployTime() != null) {

            XxlJobInfo xxlJobInfo = new XxlJobInfo();

            xxlJobInfo.setAlarmEmail("");
            xxlJobInfo.setAuthor("taotie");
            xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");// 阻塞策略
            xxlJobInfo.setExecutorRouteStrategy("FIRST");// 路由策略
            xxlJobInfo.setTriggerStatus(0);
            xxlJobInfo.setChildJobId("");
            xxlJobInfo.setMisfireStrategy("DO_NOTHING");
            xxlJobInfo.setExecutorFailRetryCount(0);
            xxlJobInfo.setExecutorTimeout(0);
            xxlJobInfo.setJobDesc("2504定时发布笔记任务");
            // 执行方式,以BEAN
            xxlJobInfo.setGlueType("BEAN");
            // 设置cron时间表达式
            xxlJobInfo.setScheduleType("CRON");
            // 时间 2025-11-14 22:00:00
            // cron 0 0 22 14 11 ? 2025
            SimpleDateFormat sdf = new SimpleDateFormat("ss mm HH dd MM ?");
            xxlJobInfo.setScheduleConf(sdf.format(grassDTO.getDeployTime()));
            // 设置执行器名称
            xxlJobInfo.setExecutorHandler("updateGrassEnableHandler");
            // 设置任务参数(定时任务执行时可以使用的参数)
            // 此处是文章id,该任务真正执行时,需要根据文章id,修改文章的状态status=1,即已发布
            xxlJobInfo.setExecutorParam(String.valueOf(grass.getId()));// id是上方插入后,主键回填的

            xxlJobUtils.addAndStart(xxlJobInfo);// 发布并启动
        } else {
            grass.setStatus(1); // 直接发布
        }

        // 添加积分信息
        R r = pointFeign.addPoint(uid, 2);
        System.out.println("Grass中调用OpenFeign提供的送积分接口的返回值: " + r);
    }

    /**
     * 分页查询种草文章
     * @param dto
     * @return
     */
    @Override
    public PageVO<GrassVO> page(GrassSearchDTO dto) {
        Page<Grass> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        Page<Grass> pageResult = grassMapper.selectPage(page,
                Wrappers.lambdaQuery(Grass.class)
                        .eq(Grass::getCheckStatus,1)// 1审核通过
                        .eq(Grass::getStatus,1));// 1 已发布
        List<Grass> records = pageResult.getRecords();
        long total = pageResult.getTotal();

        PageVO<GrassVO> pageVO = new PageVO<>();
        pageVO.setTotal(total);// 总条数

        Integer uid = UserUtils.getUid();
        // 查出当前用户的全部点赞记录
        // select * from grass_like where uid = 1
        List<GrassLike> grassLikes = grassLikeMapper.selectList(Wrappers.
                lambdaQuery(GrassLike.class)
                .eq(GrassLike::getUid, uid));
        List<Integer> grassLikeIds = grassLikes.stream().map(grass->grass.getGrassId()).collect(Collectors.toList());


        String key = String.format("like:num:%s", DateUtil.format(new Date(),"yyyyMMdd"));
        // 将List<Grass> 转成 List<GrassVO>
        List<GrassVO> dataList = records.stream().map(grass -> {
            // 从redis取出今天的点赞数
            Integer todayLikeNum = redisUtils.getCacheMapValue(key, grass.getId().toString());

            GrassVO grassVO = new GrassVO();
            grassVO.setLikeNum(todayLikeNum == null ? grass.getLikeNum() : grass.getLikeNum()+todayLikeNum);// redis当天的数+数据库取出

            grassVO.setLikeFlag(grassLikeIds.contains(grass.getId())?1:0);
            grassVO.setTitle(grass.getTitle());
            grassVO.setId(grass.getId());
            grassVO.setCoverImg(grass.getImgPath().split(",")[0]);// 取出第1张图片的url
            return grassVO;
        }).collect(Collectors.toList());
        pageVO.setDataList(dataList);
        return pageVO;
    }

    /**
     * 定时更新
     */
    @Override
    public void updateLikeNumTask() {
        System.out.println("============== 定时任务:更新点赞 ================");
        // 因为定时任务,是在第二天凌晨3点执行,将前一天的点赞数据同步到数据库
        String key = String.format("like:num:%s", DateUtil.format(DateUtil.yesterday(), "yyyyMMdd"));

        // map中key是文章id,value是该文章当天的点赞数
        Map<String, Object> cacheMap = redisUtils.getCacheMap(key);

        ArrayList<Grass> batchList = new ArrayList<>();
        final int BATCH_COUNT = 3;

        int countDown = cacheMap.size() % BATCH_COUNT == 0 ? cacheMap.size() / BATCH_COUNT : cacheMap.size() / BATCH_COUNT + 1;
        CountDownLatch countDownLatch = new CountDownLatch(countDown);// 根据批次数量创建门闩

        // 实现批量更新
        // update  .... where ....
        cacheMap.entrySet().stream().forEach(entry->{
            Integer grassId = Integer.valueOf(entry.getKey());
            Integer likeNum = (Integer) entry.getValue();
            Grass grass = new Grass();
            grass.setId(grassId);
            grass.setLikeNum(likeNum);

            batchList.add(grass);
            if (batchList.size()== BATCH_COUNT) {
                threadPoolExecutor.execute(()->{
                    try {

                        // 批量更新
                        grassMapper.updateGrassLikeNum(batchList);
                        Thread.sleep(10000);
                        System.out.println(Thread.currentThread().getName()+",更新一批点赞数到数据库..");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    } finally {
                        countDownLatch.countDown();// 每个线程执行完,计数器-1
                    }
                });
                batchList.clear();
            }
        });

        if (batchList.size() != 0) {
            // 最后,没有达到批处理的阈值,剩下的也更新
            try {
                // 批量更新
                grassMapper.updateGrassLikeNum(batchList);
                Thread.sleep(10000);
                System.out.println(Thread.currentThread().getName()+",更新最后一批点赞数到数据库..");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                countDownLatch.countDown();
            }
        }

        try {
            countDownLatch.await();// 等待上面线程执行完毕,再放行向下
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 同步到数据库后,可以删除那天的数据
        redisUtils.deleteObject(key);
        System.out.println("============== 定时任务:更新点赞(结束) ================");
    }

    @Override
    public void updateById(Grass grass) {
        grassMapper.updateById(grass);
    }
}
